package com.raorao.rpc.common.utils;

import com.raorao.rpc.common.constants.LoadBalanceEnum;
import com.raorao.rpc.common.constants.ServiceStatusEnum;
import com.raorao.rpc.common.service.ServiceInfo;
import com.raorao.rpc.properties.RpcProperty;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;

/**
 * @author raorao
 * @version 1.0
 * @description: 负载均衡工具类
 * @date 2021/11/22 15:42
 */
public class LoadBalanceUtils {

    private RpcProperty rpcProperty=null;
    private Integer pos = 0;
    private Random random = new Random();

    public LoadBalanceUtils(RpcProperty rpcProperty){
        this.rpcProperty=rpcProperty;
    }

    /**
     * @description:  数据非空判断
     * @param: source
     * @return: java.util.List<com.raorao.rpc.common.service.ServiceInfo>
     * @author raorao
     * @date: 2021/11/22 15:50
     */
    private List<ServiceInfo> init(List<ServiceInfo> source){
        if(CollectionUtils.isEmpty(source)||source.size()<1){
            return new ArrayList<>();
        }
        List<ServiceInfo> services = source.stream().filter(it -> it.getStatus() != ServiceStatusEnum.ERRO.getValue()).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(services)||services.size()<1){
            return new ArrayList<>();
        }
        return services;
    }
    
    /** 
     * @description: 轮询（Round Robin）法
     * @param:  source
     * @return: com.raorao.rpc.common.service.ServiceInfo 
     * @author raorao
     * @date: 2021/11/22 15:43
     */
    private ServiceInfo roundRobin(List<ServiceInfo> source){
        ServiceInfo server = null;
        List<ServiceInfo> serviceInfos = this.init(source);
        synchronized (pos)
        {
            if(serviceInfos.size()<1)return server;
            if (pos > serviceInfos.size())
                pos = 0;
            server = serviceInfos.get(pos);
            pos ++;
        }
        return server;
    }

    /**
     * @description: 随机（Random）法
     * @param:  source
     * @return: com.raorao.rpc.common.service.ServiceInfo
     * @author raorao
     * @date: 2021/11/22 15:43
     */
    private ServiceInfo random(List<ServiceInfo> source){
        ServiceInfo server = null;
        List<ServiceInfo> serviceInfos = this.init(source);
        if(serviceInfos.size()<1)return server;
        server = serviceInfos.get(random.nextInt(serviceInfos.size()));
        return server;
    }

    /**
     * @description: 加权轮询（Weight Round Robin）法
     * @param:  source
     * @return: com.raorao.rpc.common.service.ServiceInfo
     * @author raorao
     * @date: 2021/11/22 15:43
     */
    private ServiceInfo weightRoundRobin(List<ServiceInfo> source){
        ServiceInfo server = null;
        List<ServiceInfo> serviceInfos = this.init(source);
        if(serviceInfos.size()<1)return server;
        List<ServiceInfo> serverList = new ArrayList<>();
        for (ServiceInfo item :serviceInfos)
        {
            int weight = ServiceStatusEnum.fromVale(item.getStatus()).getWeight();
            for (int i = 0; i < weight; i++)
                serverList.add(server);
        }
        synchronized (pos)
        {
            if (pos > serverList.size())
                pos = 0;
            server = serverList.get(pos);
            pos ++;
        }
        return server;
    }

    /**
     * @description: 加权随机（Weight Random）法
     * @param:  source
     * @return: com.raorao.rpc.common.service.ServiceInfo
     * @author raorao
     * @date: 2021/11/22 15:43
     */
    private ServiceInfo weightRandom(List<ServiceInfo> source){
        ServiceInfo server = null;
        List<ServiceInfo> serviceInfos = this.init(source);
        if(serviceInfos.size()<1)return server;
        List<ServiceInfo> serverList = new ArrayList<>();
        for (ServiceInfo item :serviceInfos)
        {
            int weight = ServiceStatusEnum.fromVale(item.getStatus()).getWeight();
            for (int i = 0; i < weight; i++)
                serverList.add(server);
        }
        server = serverList.get(random.nextInt(serverList.size()));
        return server;
    }

    /**
     * @description: 获取服务
     * @param:  source
     * @return: com.raorao.rpc.common.service.ServiceInfo
     * @author raorao
     * @date: 2021/11/22 15:43
     */
    public ServiceInfo getServiceInfo(List<ServiceInfo> source){
        switch (LoadBalanceEnum.fromString(rpcProperty.getLoadBalance())){
            case RANDOM:return this.random(source);
            case ROUNDROBIN:return this.roundRobin(source);
            case WEIGHRROUND:return this.weightRandom(source);
            case WEIGHRROUNDROBIN:return this.weightRoundRobin(source);
            default:return this.random(source);
        }
    }
}
